﻿//#define _CRT_SECURE_NO_WARNINGS
//#include <stdio.h>
#include "common.h"

// debug 和 release 的区别在于 debug 模式下，编译器会在编译时
// 产生额外的调试信息，使得程序运行时可以输出更多的调试信息，方便程序的调试。
// 而 release 模式下，编译器会对代码进行优化，使得程序运行时运行速度更快，
// 但也会导致程序运行时无法输出调试信息。
#if defined(_DEBUG) || defined(DEBUG)
    #define DEBUG
#elif defined(NDEBUG) || defined(_RELEASE)
    #define RELEASE // 定义一个宏，用于区分 debug 和 release 模式
#else
    #error "未定义 DEBUG 或 NDEBUG 宏"
#endif

// 调试快捷键：
// F5 开始调试：编译并运行程序，直到遇到断点或程序结束
// F9 设置断点：在当前行设置断点，程序运行到断点处暂停
// Shift+F9 移除断点：移除当前行的断点
// Ctrl+F8 跳过断点：跳过当前断点，继续运行程序
// Ctrl+Shift+F8 恢复断点：恢复当前断点，程序运行到断点处暂停
// F10 单步调试：单步执行程序，遇到函数调用则一步执行完成
// F11 进入函数：单步进入函数，遇到下一个断点则停止
// Shift+F11 退出函数：退出当前函数，回到上一断点
// Ctrl+Shift+F9 运行到光标处：运行到光标处，遇到下一个断点则停止
// Ctrl+F5 重新编译：重新编译程序，但不运行
// Ctrl+Alt+F5 调试时停止：暂停程序的运行，方便查看变量值或调用堆栈

// 调试断点：
// 调试断点可设置条件，只有满足条件时才会暂停程序的运行。
// 断点的作用：
// 1. 定位程序运行的位置，方便程序的调试；
// 2. 监视变量的值，方便程序的运行状态的分析；
// 3. 触发程序的特定功能，方便程序的测试。

// 调试方法：
// 1. 编译时加上 -g 选项，使编译器产生调试信息；
// 2. 使用 printf() 函数输出调试信息；
// 3. 使用断点调试，设置断点，程序运行到断点处暂停，查看变量的值、调用堆栈等；
// 4. 使用单步调试，逐行执行程序，查看变量的值、调用堆栈等；
// 5. 使用调试器的调试功能，如查看变量的值、调用堆栈等。

// 调试模式开始之后（F10或F5），可以在调试窗口查看对应的变量的值、调用堆栈等。
// 常用的调试窗口主要有监视、内存窗口、调用堆栈、寄存器以及反汇编窗口等。
// 监视窗口：显示当前程序运行时变量的值。
// 内存窗口：显示当前程序运行时内存的使用情况。
// 反汇编窗口：显示当前程序的汇编代码。

void test2()
{
    int a = 10;
    int b = 20;
    int c = a + b;
    printf("c = %d\n", c);
}

void test1()
{
    test2();
}

int main()
{
    int arr1[10] = { 0 };
    int i;
    for (i = 0; i < 10; i++)
        arr1[i] = i;
    test1();

    /*int j;
    int arr2[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (j = 0; j <= 12; ++j)
    {
        arr2[j] = 0;
        printf("hehe\n"); // x86 平台下，无限循环
        // 解释：栈区内存使用由高到低，j 先使用高地址，
        // arr2 下标对应地址由底到高，这里调试可以看到 arr2[12] == j
    }
    printf("\n");*/

    // 测试自写函数实现库函数功能
    {
        print_start_line("测试自写函数实现库函数功能开始");
        char str1[20] = "********************";
        char str2[17] = "i am a 字符串";
        char* copy = my_strcpy(str1, str2);
        printf("str1 = %s，sizeof str1 = %zd\n", str1, sizeof(str1));
        printf("*str1 = %c\n", *str1);
        printf("*copy = %s\n", copy); // 输出拷贝的数组

        int t_a = 0;
        if (t_a++) // t_a 为假，if不成立，但是 ++ 运算符会执行
            printf("t_a++为真\n"); // 输出 t_a 的值
        else
            printf("t_a = %d\n", t_a); // 输出 t_a 的值

        // 测试 my_strlen 函数
        char str3[20] = "hello world";
        int len = my_strlen(str3);
        printf("str3 = %s，len = %d\n", str3, len);

        print_end_line("测试自写函数实现库函数功能结束");
    }

    // const 修饰变量的理解
    {
        print_start_line("const 变量的理解开始");
        const int a = 10; // 同 int const a = 10;
        //a = 20; // error: assignment of read-only variable 'a'
        int* pa = &a;
        *pa = 20; // 指针可以修改 const 变量的值
        printf("a = %d\n", a);
        int const* pb = &a; // 同 const int* pb = &a;
        //*b = 30; // error: assignment of read-only variable 'b'
        int const c = 40;
        pb = &c; // ok: 可以修改指针指向的地址
        printf("*pb = %d\n", *pb);
        //*b = 50; // error: assignment of read-only variable 'b'
        /* 所以：const 关键字的作用就是限制变量的修改，对于指针变量，
            放在 * 前面时，可以修改指针指向的地址，但不能修改指针指向的值。*/
        int* const pd = &a; // const 放在 * 号后面
        //pd = &c; // error: assignment of read-only variable 'd'
        *pd = 60; // ok: 可以修改指针指向的值
        printf("a = %d\n", a);
        /* 所以：const 放在 * 号后面时，表示指针指向的值是可以修改的，
            但是指针本身的地址是不能修改的。 */
        const int* const pcd = &a; // const 放在 * 号两边
        //pcd = &c; // error: assignment of read-only variable 'd'
        //*pcd = 70; // error: assignment of read-only variable 'd'
        /*所以：* 两边都加 const 时，指针本身的地址和指向的值都不能修改*/

        print_end_line("const 变量的理解结束");
    }

    // 作业部分
    {
        print_start_line("作业部分开始");
        // 三条边能否构成三角形的的判断
        /* char result[20] = {0};
        triangle_judge(result);
        printf("%s\n", result); */

        // 指针实现字符串逆序
        char str_rev[20] = "hello world";
        reverse_string(str_rev);
        printf("%s\n", str_rev);

        // 计算一个同值数列前 n 项之和
        printf("同值数列前 2 的前 3 项之和为：%d\n", sum_like_num(2, 3));

        // 打印 1 到 10000 之间的水仙花数
        printf("1 到 100000 之间的水仙花数个数为：%d\n",
            narcissistic(1, 100000));

        // 打印菱形
        print_diamond(18);

        // 喝汽水问题
        printf("喝汽水问题测试结果为：%d\n",
            empty_bottle(1, 3, 20));

        print_end_line("作业部分结束");
    }

    return 0;
}
